﻿using System;
using System.Collections.Generic;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;

namespace DA.AnimGraph {

    public struct TimelineRuntimeContext {
        // 事件常量（配置）信息
        public TimelineEventState state;
        public TimelineEventFlag flag;
        public float time; // 归一化的时间
        // 当次调用信息
        public float runtimeSliceEndTime; // 此帧运行到了哪里，是百分比形式。如果是确保触发引起的执行，则为-1
        public float runtimeDuration; // 动画片段时长
    }

    public class TimelineEventRegister {

        private static TimelineEventRegister instance;
        public static TimelineEventRegister GetInstance() {
            if (instance == null) {
                instance = new();
            }
            return instance;
        }

        // typeUuid, <target, state, flag, parameter>
        public Dictionary<AUID, Action<object, TimelineRuntimeContext, byte[]>> eventFunction { get; private set; }
        private TimelineEventRegister() {
            eventFunction = new();
        }

    }

    public class TimelineRuntimeProcessor {

        class TimelineData {
            public readonly ReadedTimelineEvent[] events;
            public readonly ReadedTimelineEvent[] alwaysEvents;
            public MileStone mileStone;
            public TimelineData(ReadedTimelineEvent[] events, ReadedTimelineEvent[] alwaysEvents) {
                this.events = events;
                this.alwaysEvents = alwaysEvents;
                mileStone = new();
            }
        }

        struct MileStone {
            public int nextCheckIndex; // 上一次触发的事件序号+1，即下一次开始检查的位置，用于加快遍历和结束触发
        }

        private readonly object target;
        private Dictionary<AUID, TimelineData> timelineDict; // timelineUuid
        private TimelineEventRegister timelineEventRegister;


        public TimelineRuntimeProcessor(object target) {
            Debug.Assert(target != null);
            timelineDict = new();
            this.target = target;
            timelineEventRegister = TimelineEventRegister.GetInstance();
        }

        public void Append(ReadedTimeline[] timeline) {
            for (int i = 0; i < timeline.Length; i++) {
                var t = timeline[i];
                if (!timelineDict.ContainsKey(t.uuid)) {
                    timelineDict.Add(t.uuid, new TimelineData(t.events, t.alwaysEvents));
                }
            }
        }

        public void Check(in UnsafeList<TimelineSlice> TimelineSlices) {
            for (int i = 0; i < TimelineSlices.Length; i++) {
                ref var slice = ref TimelineSlices.ElementAt(i);
                var timelineUuid = slice.uuid;
                if (timelineDict.TryGetValue(timelineUuid, out TimelineData tlData)) {
                    if (tlData.events.Length > 0) {
                        if (slice.isEnter) {
                            // 刚进入也可能不是从头开始播放，这种情况也只能从头开始检查
                            tlData.mileStone.nextCheckIndex = 0; // 以防赋0有漏
                        }
                        if (slice.isExit) {
                            // 确保执行触发，执行已执行start的end，和确保执行的evt
                            for (int j = tlData.mileStone.nextCheckIndex; j < tlData.events.Length; j++) {
                                var evt = tlData.events[j];
                                if (evt.flag == TimelineEventFlag.TriggerBeforeExit // 设置为确保触发
                                    || (evt.state == TimelineEventState.HoldEnd && evt.pairIndex < tlData.mileStone.nextCheckIndex) // 范围事件，起点已经触发过了
                                   ) {
                                    if (timelineEventRegister.eventFunction.TryGetValue(evt.typeUuid, out var func)) {
                                        func?.Invoke(target, new TimelineRuntimeContext() {
                                            state = evt.state,
                                            flag = evt.flag,
                                            time = evt.time,
                                            runtimeSliceEndTime = slice.endTime,
                                            runtimeDuration = slice.duration,
                                        }, evt.parameter);
                                    }
                                }
                            }
                            tlData.mileStone.nextCheckIndex = 0;
                        } else {
                            // 正常时间轴执行
                            int checkIndex = tlData.mileStone.nextCheckIndex;
                            for (int j = 0; j < tlData.events.Length; j++) {
                                if (checkIndex >= tlData.events.Length) checkIndex = 0;

                                var evt = tlData.events[checkIndex];
                                bool isIn = slice.IsInSlice(evt.time);
                                isIn = isIn || slice.IsInSliceIfOnEnter(evt.time);
                                if (isIn && timelineEventRegister.eventFunction.TryGetValue(evt.typeUuid, out var func)) {
                                    func?.Invoke(target, new TimelineRuntimeContext() {
                                        state = evt.state,
                                        flag = evt.flag,
                                        time = evt.time,
                                        runtimeSliceEndTime = slice.endTime,
                                        runtimeDuration = slice.duration,
                                    }, evt.parameter);
                                } else if (evt.time > slice.endTime) {
                                    break;
                                }

                                ++checkIndex;
                                tlData.mileStone.nextCheckIndex = checkIndex;
                            }
                        }
                    }
                    if (tlData.alwaysEvents.Length > 0) {
                        // 执行Always事件(每帧执行)
                        for (int j = 0; j < tlData.alwaysEvents.Length; j++) {
                            var evt = tlData.alwaysEvents[j];
                            if (timelineEventRegister.eventFunction.TryGetValue(evt.typeUuid, out var func)) {
                                slice.Solve();
                                func?.Invoke(target, new TimelineRuntimeContext() {
                                    state = evt.state,
                                    flag = evt.flag,
                                    time = evt.time,
                                    runtimeSliceEndTime = slice.endTime,
                                    runtimeDuration = slice.duration,
                                }, evt.parameter);
                            }
                        }
                    }
                }
            }
        }

    }
}
